<?php
/**
 * ValidationParser allows you to register validators with the system.
 *
 */
class ValidationParser extends sfValidatorConfigHandler
{
	/**
	 * get a configuration value
	 *
	 * @param string $key
	 * @return string
	 */
	protected function getAppConfigValue($key) {
  	$lastForm = LastForm::singleton();
  	$moduleName = $lastForm->getModule();
  	$actionName = $lastForm->getAction();
		return sfConfig::has("mod_$moduleName"."_$key") ? sfConfig::get("mod_$moduleName"."_$key") : (sfConfig::has("app_$key") ? sfConfig::get("app_$key") : null);
	}
	
  /**
   * Execute this configuration handler.
   *
   * @param string An absolute filesystem path to a configuration file.
   *
   * @return string Data to be written to a cache file.
   *
   * @throws sfConfigurationException If a requested configuration file does not exist or is not readable.
   * @throws sfParseException If a requested configuration file is improperly formatted.
   */
  public function execute($configFile)
  {
  	$configFiles = array($configFile);

    // parse the yaml
    $config = $this->parseYamls($configFiles);
    if (isset($config['fields'])) {
	    $this->convertAlternate2Standard($config);
    }
    if (isset($config['fields'])) {
      $error = sprintf('Configuration file "%s" is missing "fields" category', $configFile);
      throw new sfParseException($error);
    }
    
    // init our data, includes, methods, names and validators arrays
    $data       = array();
    $includes   = array();
    $methods    = array();
    $names      = array();
    $validators = array();

    // get a list of methods and their registered files/parameters
    foreach ($config['methods'] as $method => $list)
    {
      $method = strtoupper($method);

      if (!isset($methods[$method]))
      {
        // make sure that this method is GET or POST
        if ($method != 'GET' && $method != 'POST')
        {
          // unsupported request method
          $error = sprintf('Configuration file "%s" specifies unsupported request method "%s"', $configFiles[0], $method);

          throw new sfParseException($method);
        }

        // create our method
        $methods[$method] = array();
      }

      if (!count($list))
      {
        // we have an empty list of names
        continue;
      }

      // load name list
      $this->loadNames($configFiles, $method, $methods, $names, $config, $list);
    }

    // load attribute list
    $this->loadAttributes($configFiles, $methods, $names, $validators, $config, $list);

    $data[] = '$lastForm = LastForm::singleton();';
    $data[] = '$formModuleName = $lastForm->getModule();';
    $data[] = '$formActionName = $lastForm->getAction();';
    $data[] = '$formMethod = $lastForm->getMethod();';
    $data[] = '$validators = array();';
    $data[] = '$fields = array();';
    $data[] = '$fieldValidators = array();';
    $data[] = '$fieldEvents = array();';
    $data[] = '$fallback = ' . ($this->getAppConfigValue('clientside_validation_fallback') ? '"'.$this->getAppConfigValue('clientside_validation_fallback').'"' : 'null') . ';';
    $data[] = '$alertImageURI = ' . ($this->getAppConfigValue('clientside_validation_alerticon') ? '"'.$this->getAppConfigValue('clientside_validation_alerticon').'"' : 'null') . ';';
    $data[] = '$event = ' . ($this->getAppConfigValue('clientside_validation_event') ? var_export($this->getAppConfigValue('clientside_validation_event'), true) : 'null') . ';';
    
    $data[] = "if (\$formMethod == 'GET')";
    $data[] = "{";
    $ret = $this->generateRegistration('GET', $data, $methods, $names, $validators); // generate GET file/parameter data
    $data[] = "}";
    $data[] = "else if (\$formMethod == 'POST')";
    $data[] = "{";
    $ret = $this->generateRegistration('POST', $data, $methods, $names, $validators); // generate POST file/parameter data
    $data[] = "}";
    
    // compile data
    $retval = sprintf("<?php\n".
                      "// auto-generated by ValidationParser\n".
                      "// date: %s\n%s\n%s\n?>", date('Y/m/d H:i:s'),
                      implode("\n", $includes), implode("\n", $data));

                      return $retval;
  }

  /**
   * Generate raw cache data.
   *
   * @param string A request method.
   * @param array  The data array where our cache code will be appended.
   * @param array  An associative array of request method data.
   * @param array  An associative array of file/parameter data.
   * @param array  A validators array.
   * 
   * @return boolean Returns true if there is some validators for this file/parameter
   */
  protected function generateRegistration($method, &$data, &$methods, &$names, &$validators)
  {
    if (!isset($methods[$method]))
    {
      $methods[$method] = array();
    }

    // determine which validators we need to create for this request method
    foreach ($methods[$method] as $name)
    {
      if (preg_match('/^([a-z0-9_-]+)\{([a-z0-9\s_-]+)\}$/i', $name, $match))
      {
        // this file/parameter has a parent
        $subname = $match[2];
        $parent  = $match[1];

        $valList = $names[$parent][$subname]['validators'];
      }
      else
      {
        // no parent
        $valList = $names[$name]['validators'];
      }

      if ($valList == null)
      {
        // no validator list for this file/parameter
        continue;
      }

      foreach ($valList as $valName)
      {
        if (isset($validators[$valName]) && !isset($validators[$valName][$method]))
        {
          // retrieve this validator's info
          $validator =& $validators[$valName];

          $data[] = sprintf("  \$validators['%s'] = array('class'=>'%s', 'parameters'=>%s);",
          $valName, $validator['class'], $validator['parameters']);

          // mark this validator as created for this request method
          $validators[$valName][$method] = true;
        }
      }
    }

    foreach ($methods[$method] as $name)
    {
      if (preg_match('/^([a-z0-9_-]+)\{([a-z0-9\s_-]+)\}$/i', $name, $match))
      {
        // this file/parameter has a parent
        $subname = $match[2];
        $parent  = $match[1];
        $name    = $match[2];

        $attributes = $names[$parent][$subname];
      }
      else
      {
        // no parent
        $attributes = $names[$name];
      }

      // register file/parameter
      $data[] = sprintf("  \$validatorFields['%s'] = array(%s, %s, %s, %s, %s); // required, message, parent, group, isFile",
      $name,
      (($attributes['required'] !== null)?$attributes['required']:0),
      $attributes['required_msg'],
      $attributes['parent'], $attributes['group'],
      $attributes['file']);

      // register validators for this file/parameter
      $fieldValidators = array();
      foreach ($attributes['validators'] as &$validator)
      {
        $fieldValidators [] = "array('$validator', ".$attributes['parent'].")";
      }
      $data[] = sprintf("  \$fieldValidators['%s'] = array(%s); // array of arrays of (validator, parent)", $name, implode(', ', $fieldValidators));
      
      // register events
	  	$lastForm = LastForm::singleton();
	  	$moduleName = $lastForm->getModule();
	  	$actionName = $lastForm->getAction();
      $actionsConfig = sfConfig::get("mod_" . $moduleName . "_clientside_validation_actions");
	    if($actionsConfig) {
	    	if(isset($actionsConfig[$actionName]['fields'][$name])) {
	    		$data[] = "  \$fieldEvents['$name'] = " . var_export($actionsConfig[$actionName]['fields'][$name], true) . ";";
	    	}
	    }

    }

    return count($methods[$method]) ? true : false;
  }

}

